package com.bmslz.monitor.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bmslz.monitor.base.CommonConstant;
import com.bmslz.monitor.base.dto.DoAlarmDto;
import com.bmslz.monitor.base.entity.*;
import com.bmslz.monitor.base.util.StringUtils;
import com.bmslz.monitor.helper.AlarmNotifyHelper;
import com.bmslz.monitor.helper.LoginHelper;
import com.bmslz.monitor.mapper.ProjectAlarmMapper;
import com.bmslz.monitor.service.IAlarmModelRuleService;
import com.bmslz.monitor.service.IProjectAlarmService;
import com.bmslz.monitor.service.IProjectService;
import com.bmslz.monitor.service.IUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @Description: 项目告警
 * @Author: bobo
 * @Date: 2025-08-10
 * @Version: V1.0
 */
@Slf4j
@Service
public class ProjectAlarmServiceImpl extends ServiceImpl<ProjectAlarmMapper, ProjectAlarm> implements IProjectAlarmService {

    @Autowired
    private IProjectService projectService;
    @Autowired
    private IAlarmModelRuleService alarmModelRuleService;
    @Autowired
    private AlarmNotifyHelper alarmNotifyHelper;
    @Autowired
    private IUserService userService;

    @Override
    public void handle(List<ProjectData> projectDataList, Project project) {
        for (ProjectData projectData : projectDataList) {
            this.handle(projectData, project);
        }
    }

    @Override
    public void handle(ProjectData projectData, Project project) {
        //从这里开始需要用户数据隔离 所以把上下文改为当前模型拥有者的id
        User user = userService.getById(project.getUserId());
        LoginHelper.openUserIsolation(user);

        String projectNo = projectData.getProjectNo();
        String attKey = projectData.getAttKey();
        String attValue = projectData.getAttValue();
        String projectName = projectData.getProjectName();
        String attName = projectData.getAttName();
        String userId = project.getUserId();
        String projectId = project.getId();

        String alarmModelId = project.getAlarmModelId();
        //查询当前测点正在告警中的记录(原来已经触发的告警记录)
        List<ProjectAlarm> oldAlarms = this.alarmIng(projectNo, attKey);
        //测点正在告警中的告警规则编号
        List<String> oldAlarmsRuleId = oldAlarms.stream().map(ProjectAlarm::getAlarmRuleId).collect(Collectors.toList());
        //匹配告警规则 返回空说明没设置规则或者没触发告警（当前这包测点数据触发的告警规则）
        List<AlarmModelRule> nowRule = alarmModelRuleService.matchRule(alarmModelId, projectNo, attKey, attValue);
        //当前匹配到的规则编号（即将要预警的内容）
        List<String> nowRuleId = nowRule.stream().map(AlarmModelRule::getId).collect(Collectors.toList());

        //正在预警中 但是本次测点数据没有击中预警模板的情况 说明需要解除告警
        List<ProjectAlarm> closeAlarm = oldAlarms.stream()
                .filter(alarm -> !nowRuleId.contains(alarm.getAlarmRuleId()))
                .collect(Collectors.toList());

        //正在预警中 但是本次测点数据继续击中相同预警模板的情况 说明需要继续预警，但是要判断是否静默
        List<ProjectAlarm> reAlarm = oldAlarms.stream()
                .filter(alarm -> nowRuleId.contains(alarm.getAlarmRuleId()))
                .collect(Collectors.toList());

        //击中预警规则 但是没有在告警，说明是新告警
        List<AlarmModelRule> newAlarmRule = nowRule.stream()
                .filter(alarm -> !oldAlarmsRuleId.contains(alarm.getId()))
                .collect(Collectors.toList());

        closeAlarm(closeAlarm.stream().map(item -> {
            DoAlarmDto doAlarmDto = new DoAlarmDto();
            doAlarmDto.setAlarmModelRule(alarmModelRuleService.getById(item.getAlarmRuleId()));
            doAlarmDto.setProjectAlarm(item);
            doAlarmDto.setProjectId(projectId);
            doAlarmDto.setProjectNo(projectNo);
            doAlarmDto.setProjectName(projectName);
            doAlarmDto.setAlarmValue(attValue);
            doAlarmDto.setAttName(attName);
            doAlarmDto.setUserId(userId);
            return doAlarmDto;
        }).collect(Collectors.toList()));

        reAlarm(reAlarm.stream().map(item -> {
            DoAlarmDto doAlarmDto = new DoAlarmDto();
            doAlarmDto.setAlarmModelRule(alarmModelRuleService.getById(item.getAlarmRuleId()));
            item.setAlarmValue(attValue);
            doAlarmDto.setProjectAlarm(item);
            doAlarmDto.setAlarmValue(attValue);
            doAlarmDto.setProjectId(projectId);
            doAlarmDto.setProjectNo(projectNo);
            doAlarmDto.setProjectName(projectName);
            doAlarmDto.setAttName(attName);
            doAlarmDto.setUserId(userId);
            return doAlarmDto;
        }).collect(Collectors.toList()));

        newAlarm(newAlarmRule.stream().map(item -> {
            DoAlarmDto doAlarmDto = new DoAlarmDto();
            doAlarmDto.setAlarmModelRule(item);
            doAlarmDto.setProjectId(projectId);
            doAlarmDto.setProjectNo(projectNo);
            doAlarmDto.setProjectName(projectName);
            doAlarmDto.setAlarmValue(attValue);
            doAlarmDto.setAttName(attName);
            doAlarmDto.setUserId(userId);
            return doAlarmDto;
        }).collect(Collectors.toList()));
    }

    @Async
    @Override
    public void handleAsync(List<ProjectData> projectDataList, Project project) {
        handle(projectDataList, project);
    }

    @Override
    public List<ProjectAlarm> alarmIng(String projectNo, String attKey) {
        return baseMapper.selectList(new LambdaQueryWrapper<ProjectAlarm>()
                .eq(ProjectAlarm::getProjectNo, projectNo).eq(StringUtils.isNotEmpty(attKey),ProjectAlarm::getAttKey, attKey)
                .eq(ProjectAlarm::getStatus, CommonConstant.ALARM_STATUS_ING));
    }

    @Override
    public List<ProjectAlarm> alarmIngByProjectId(String projectId, String attKey) {
        return baseMapper.selectList(new LambdaQueryWrapper<ProjectAlarm>()
                .eq(ProjectAlarm::getProjectId, projectId).eq(StringUtils.isNotEmpty(attKey),ProjectAlarm::getAttKey, attKey)
                .eq(ProjectAlarm::getStatus, CommonConstant.ALARM_STATUS_ING));
    }

    @Override
    public Long totalIng() {
        return this.count(new LambdaQueryWrapper<ProjectAlarm>()
                .eq(ProjectAlarm::getStatus, CommonConstant.ALARM_STATUS_ING));
    }

    public void closeAlarm(List<DoAlarmDto> doAlarmList) {
        for (DoAlarmDto doAlarmDto : doAlarmList) {
            ProjectAlarm updateProjectAlarm = new ProjectAlarm();
            updateProjectAlarm.setId(doAlarmDto.getProjectAlarm().getId());
            updateProjectAlarm.setStatus(CommonConstant.ALARM_STATUS_CLOSE);
            doAlarmDto.getProjectAlarm().setStatus(CommonConstant.ALARM_STATUS_CLOSE);
            baseMapper.updateById(updateProjectAlarm);
            //单独维护项目告警状态
            projectService.refreshAlarmIng(doAlarmDto.getProjectId());
        }
        //发送解除告警通知
        alarmNotifyHelper.sendNotify(doAlarmList);
    }

    private void newAlarm(List<DoAlarmDto> doAlarmDtoList) {
        List<ProjectAlarm> projectAlarms = new ArrayList<>();
        for (DoAlarmDto doAlarmDto : doAlarmDtoList) {
            ProjectAlarm projectAlarm = new ProjectAlarm();
            projectAlarm.setUserId(doAlarmDto.getUserId());
            projectAlarm.setAlarmRuleId(doAlarmDto.getAlarmModelRule().getId());
            projectAlarm.setAlarmRuleNo(doAlarmDto.getAlarmModelRule().getRuleNo());
            projectAlarm.setAlarmValue(doAlarmDto.getAlarmValue());
            projectAlarm.setGrade(doAlarmDto.getAlarmModelRule().getGrade());
            projectAlarm.setProjectId(doAlarmDto.getProjectId());
            projectAlarm.setProjectNo(doAlarmDto.getProjectNo());
            projectAlarm.setRuleValue(doAlarmDto.getAlarmModelRule().getDataValue());
            projectAlarm.setAttKey(doAlarmDto.getAlarmModelRule().getAttKey());
            projectAlarm.setAttName(doAlarmDto.getAttName());
            projectAlarm.setProjectName(doAlarmDto.getProjectName());
            projectAlarm.setStatus(CommonConstant.ALARM_STATUS_ING);
            projectAlarm.setCreateTime(new Date());
            doAlarmDto.setProjectAlarm(projectAlarm);
            this.save(projectAlarm);
            //单独维护项目告警状态
            projectService.refreshAlarmIng(doAlarmDto.getProjectId());
        }
        //发送告警通知
        alarmNotifyHelper.sendNotify(doAlarmDtoList);
    }

    private void reAlarm(List<DoAlarmDto> reAlarmDtoList) {
        for (DoAlarmDto doAlarmDto : reAlarmDtoList) {
            ProjectAlarm projectAlarm = doAlarmDto.getProjectAlarm();
            AlarmModelRule alarmModelRule = doAlarmDto.getAlarmModelRule();

            Date oldAlarmTime = projectAlarm.getCreateTime();
            Date reAlarmTime = new Date();
            //静默时间 分钟
            Integer silenceTime = alarmModelRule.getSilenceTime();
            //静默时间内不重复生成预警 超过静默时间的重复预警
            if (reAlarmTime.getTime() - oldAlarmTime.getTime() >= silenceTime * 60 * 1000) {
                //关闭老告警
                ProjectAlarm update = new ProjectAlarm();
                update.setId(projectAlarm.getId());
                update.setStatus(CommonConstant.ALARM_STATUS_CLOSE);
                baseMapper.updateById(update);
                //新增告警
                List<DoAlarmDto> doAlarmDtoList = new ArrayList<>();
                doAlarmDtoList.add(doAlarmDto);
                newAlarm(doAlarmDtoList);
            } else {
                log.info("静默时间内不重复生成预警");
            }
            //单独维护项目告警状态
            projectService.refreshAlarmIng(doAlarmDto.getProjectId());
        }
    }
}
